-- Buffer
-- Create by Zouyb
-- 二进制读写支持

Buffer = Buffer or {}

-- 建立一个大小为size字节的buffer
function Buffer.create(size)
    local str = {};
    if type(size) == "number" then
        str.value = string.rep(string.char(0), size);
    elseif type(size) == "string" then
        str.value = size;
    end
    return str;
end

-- 将字符串转换为二进制数据
function Buffer.createFromString(s)
    local str = {};
    str.value = s;
    return str;
end

-- 将十六进制表示的字符串，转换为二进制串
function Buffer.fromHex(hex)
    buffer = Buffer.create(0);
    for i = 1, string.len(hex) - 1, 2 do
        local n = tonumber(string.sub(hex, i, i + 1), 16);
        Buffer.set8(buffer, (i + 1) / 2, n);
    end

    return buffer;
end

-- 转换为十六进制表示
function Buffer.toHex(buf)
    local out = "";
    for i = 1, string.len(buf.value) do
        out = out .. string.format("%02X", string.byte(buf.value, i)) .. " "
    end

    -- 去掉最后面的空格
    out = string.sub(out, 1, string.len(out) - 1);
    return out;
end

-- buffer的大小
function Buffer.size(buf)
    return string.len(buf.value);
end

-- buffer的原始数据
function Buffer.byte(buf)
    return buf.value;
end

-- 截取一段
function Buffer.sub(buf, start, endi)
    buf.value = string.sub(buf.value, start, endi);
    return buf;
end

-- 附加一段上去
function Buffer.append(buf, str)
    if type(str) == "string" then
        buf.value = buf.value .. str;
    else
        buf.value = buf.value .. str.value;
    end
    return buf;
end

-- 从buffer中取得一个整数
function getnum(b, start, size, unsign)
    local sum = 0
    for i = start, start + size - 1, 1 do
        sum = sum * 256 + string.byte(b.value, i);
    end

    if not unsign and string.byte(b.value, start) > 127 then
        sum = sum - math.ldexp(1, 8 * size);
    end
    return sum
end

-- 从buffer中截取一段二进制数转化为整数
function Buffer.getsubnum(b, start, size)
    local sum = 0
    for i = start, start + size - 1, 1 do
        sum = sum * 2 + string.byte(b.value, i);
    end

    return sum
end

-- 设置一个整数到buffer中，如果buffer太小，会自动进行扩充
local function setnum(b, start, va, size, unsign)
    local v = ""
    local x = math.floor(va);

    if unsign or x >= 0 then
        for i = 1, size do
            v = string.char(x % 256) .. v; x = math.floor(x / 256)
        end
    else-- x < 0
        x = -x
        local carry = 1
        for i = 1, size do
            local c = 255 - (x % 256) + carry
            if c == 256 then
                c = 0; carry = 1
            else
                carry = 0
            end
            v = string.char(c) .. v;
            x = math.floor(x / 256)
        end
    end

    -- 填充进来（旧的数据被覆盖）
    local len = string.len(v);
    if (start > Buffer.size(b)) then
        b.value = b.value .. string.rep(string.char(0), start - Buffer.size(b));
    end
    if start + len - 1 >= Buffer.size(b) then
        -- 原来的长度不够填充，直接扩展出去
        b.value = string.sub(b.value, 1, start - 1) .. v;
    else
        -- 覆盖掉
        b.value = string.sub(b.value, 1, start - 1) .. v .. string.sub(b.value, start + len, -1);
    end
end

-- 取得一个字节
function Buffer.get8(b, start)
    return getnum(b, start, 1);
end

function Buffer.getu8(b, start)
    return getnum(b, start, 1, true);
end

-- 写入一个字节
function Buffer.set8(b, start, v)
    setnum(b, start, v, 1);
end

-- 取得一个2字节整数
function Buffer.get16(b, start)
    return getnum(b, start, 2);
end

-- 写入一个2字节整数
function Buffer.set16(b, start, v)
    setnum(b, start, v, 2);
end

-- 取得一个2字节无符号整数
function Buffer.getu16(b, start)
    return getnum(b, start, 2, true);
end

-- 设置一个2字节无符号整数
function Buffer.setu16(b, start, v)
    setnum(b, start, v, 2, true);
end

-- 取得一个4字节整数
function Buffer.get32(b, start)
    return getnum(b, start, 4);
end

function Buffer.getu32(b, start)
    return getnum(b, start, 4, true);
end

-- 设置一个4字节整数
function Buffer.set32(b, start, v)
    setnum(b, start, v, 4);
end

function Buffer.setu32(b, start, v)
    setnum(b, start, v, 4, true);
end

-- 取得一个字符串
function Buffer.getstr(b, start, length)
    return string.sub(b.value, start, start + length - 1);
end

-- 设置字符串(旧的数据将被覆盖，如果长度不足自动补充)
function Buffer.setstr(b, start, length, str)
    local v = string.sub(str, 1, length);
    local len = string.len(v);
    if (start > Buffer.size(b)) then
        b.value = b.value .. string.rep(string.char(0), start - Buffer.size(b));
    end
    if start + len - 1 >= Buffer.size(b) then
        -- 原来的长度不够填充，直接扩展出去
        b.value = string.sub(b.value, 1, start - 1) .. v;
    else
        -- 覆盖掉
        b.value = string.sub(b.value, 1, start - 1) .. v .. string.sub(b.value, start + len, -1);
    end
end

--测试代码
function Buffer.test()
    if _G["TEST"] ~= true then return; end;

    -- fromHex和toHex接口
    assert(Buffer.toHex(Buffer.fromHex("FF")) == "FF" and
        Buffer.toHex(Buffer.fromHex("FFAB")) == "FF AB",
        "测试fromHex/toHex失败");

    -- size接口
    assert(Buffer.size(Buffer.fromHex("FF")) == 1, "测试size失败");

    -- 测试set接口
    buffer = Buffer.create(0);
    Buffer.set8(buffer, 1, 128);
    assert(Buffer.toHex(buffer) == "80", "测试set8失败")

    buffer = Buffer.create(0);
    Buffer.set16(buffer, 1, 1280);
    assert(Buffer.toHex(buffer) == "05 00", "测试set16失败");

    buffer = Buffer.create(0);
    Buffer.set32(buffer, 1, 1280);
    assert(Buffer.toHex(buffer) == "00 00 05 00", "测试set32失败");

	buffer = Buffer.create(0);
    Buffer.setstr(buffer, 1, 3, "abc");
    assert(Buffer.toHex(buffer) == "61 62 63", "测试setstr失败");

    print("*** Buffer功能测试通过 ***")
end
